<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="XauBn" id="XauBn"><span data-lake-id="u1302c895" id="u1302c895">典型回答</span></h1>
  <p data-lake-id="ub11a66bc" id="ub11a66bc"><br></p>
  <p data-lake-id="u55504603" id="u55504603"><span data-lake-id="ue8480b9b" id="ue8480b9b">假设我们有一张开通表，里面记录了用户的服务开通记录 service_record，主要有以下几个字段</span></p>
  <p data-lake-id="ue45602b0" id="ue45602b0"><br></p>
  <table data-lake-id="l3i4q" id="l3i4q" margin="true" width-mode="contain" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="250">
    <col width="250">
    <col width="250">
   </colgroup>
   <tbody>
    <tr data-lake-id="u658a2236" id="u658a2236">
     <td data-lake-id="u731c460d" id="u731c460d">
      <p data-lake-id="udbc9f596" id="udbc9f596"><span data-lake-id="ud3280677" id="ud3280677">字段</span></p></td>
     <td data-lake-id="u75d2b985" id="u75d2b985">
      <p data-lake-id="u4e7816ec" id="u4e7816ec"><span data-lake-id="uea4ab87c" id="uea4ab87c">类型</span></p></td>
     <td data-lake-id="u1d5c91a8" id="u1d5c91a8">
      <p data-lake-id="ub06f6555" id="ub06f6555"><span data-lake-id="ue9d2f6f7" id="ue9d2f6f7">是否可空</span></p></td>
    </tr>
    <tr data-lake-id="ubaa5b33e" id="ubaa5b33e">
     <td data-lake-id="ue91b0cd8" id="ue91b0cd8">
      <p data-lake-id="u6b614bf0" id="u6b614bf0"><span data-lake-id="u150ee0bc" id="u150ee0bc">id</span></p></td>
     <td data-lake-id="u96b32247" id="u96b32247">
      <p data-lake-id="uabec93b5" id="uabec93b5"><span data-lake-id="ub23f723a" id="ub23f723a">long</span></p></td>
     <td data-lake-id="u1b657612" id="u1b657612">
      <p data-lake-id="u1ea3d957" id="u1ea3d957"><span data-lake-id="u840db1d2" id="u840db1d2">否</span></p></td>
    </tr>
    <tr data-lake-id="u4b9ceb4d" id="u4b9ceb4d" style="height: 37px">
     <td data-lake-id="u4f40f34c" id="u4f40f34c">
      <p data-lake-id="u319bc8be" id="u319bc8be"><span data-lake-id="u49e2a4f5" id="u49e2a4f5">gmt_create</span></p></td>
     <td data-lake-id="u8f5a14d7" id="u8f5a14d7">
      <p data-lake-id="ued290f64" id="ued290f64"><span data-lake-id="ue801a91c" id="ue801a91c">date</span></p></td>
     <td data-lake-id="ub2987bb1" id="ub2987bb1">
      <p data-lake-id="uc6a96230" id="uc6a96230"><span data-lake-id="uad366426" id="uad366426">否</span></p></td>
    </tr>
    <tr data-lake-id="u80ec284b" id="u80ec284b" style="height: 37px">
     <td data-lake-id="u21a74df7" id="u21a74df7">
      <p data-lake-id="u52e09db9" id="u52e09db9"><span data-lake-id="ub957c6e9" id="ub957c6e9">gmt_modified</span></p></td>
     <td data-lake-id="ucc55c9b6" id="ucc55c9b6">
      <p data-lake-id="u53a9278a" id="u53a9278a"><span data-lake-id="u2cd6c522" id="u2cd6c522">date</span></p></td>
     <td data-lake-id="u7344be9e" id="u7344be9e">
      <p data-lake-id="uaefb51af" id="uaefb51af"><span data-lake-id="ucc1c3151" id="ucc1c3151">否</span></p></td>
    </tr>
    <tr data-lake-id="u3c6bcff0" id="u3c6bcff0" style="height: 37px">
     <td data-lake-id="u330c4652" id="u330c4652">
      <p data-lake-id="u76988e39" id="u76988e39"><span data-lake-id="u1c9a4409" id="u1c9a4409">user_id</span></p></td>
     <td data-lake-id="u39945117" id="u39945117">
      <p data-lake-id="u666a1dea" id="u666a1dea"><span data-lake-id="ufeeb141d" id="ufeeb141d">varchar</span></p></td>
     <td data-lake-id="u3fde8f00" id="u3fde8f00">
      <p data-lake-id="u429a997f" id="u429a997f"><span data-lake-id="ufb389b6a" id="ufb389b6a">否</span></p></td>
    </tr>
    <tr data-lake-id="udf08e44f" id="udf08e44f" style="height: 37px">
     <td data-lake-id="u00c7ca7b" id="u00c7ca7b">
      <p data-lake-id="u28d30cec" id="u28d30cec"><span data-lake-id="u10b4bab0" id="u10b4bab0">product_code</span></p></td>
     <td data-lake-id="u9aee45a1" id="u9aee45a1">
      <p data-lake-id="u19166fab" id="u19166fab"><span data-lake-id="ud170e510" id="ud170e510">varchar</span></p></td>
     <td data-lake-id="u4dce1da3" id="u4dce1da3">
      <p data-lake-id="u7c43fd6a" id="u7c43fd6a"><span data-lake-id="u95a1b638" id="u95a1b638">否</span></p></td>
    </tr>
    <tr data-lake-id="u504358dc" id="u504358dc" style="height: 37px">
     <td data-lake-id="u805e6cc8" id="u805e6cc8">
      <p data-lake-id="u1615b0ca" id="u1615b0ca"><span data-lake-id="u189cd9dd" id="u189cd9dd">state</span></p></td>
     <td data-lake-id="u6592ce52" id="u6592ce52">
      <p data-lake-id="ub452fb3e" id="ub452fb3e"><span data-lake-id="u5e81c5ab" id="u5e81c5ab">varchar</span></p></td>
     <td data-lake-id="uc2ee296d" id="uc2ee296d">
      <p data-lake-id="u701ce74f" id="u701ce74f"><span data-lake-id="uca1963f7" id="uca1963f7">否</span></p></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="u5c3bb65a" id="u5c3bb65a"><span data-lake-id="u7648f2de" id="u7648f2de">然后如果有用户退出的时候，我们需要记录用户的退出状态，这时候一般我们都不能做物理删除的，因为数据库的物理删除意味着数据都没有了，那么后续的各种数据分析，报表等都没办法做了，所以大多数选择逻辑删除，那么就是引入一个新的字段：</span></p>
  <p data-lake-id="u27e5e41e" id="u27e5e41e"><span data-lake-id="u44115643" id="u44115643">​</span><br></p>
  <table data-lake-id="QL1tq" id="QL1tq" margin="true" width-mode="contain" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="250">
    <col width="250">
    <col width="250">
   </colgroup>
   <tbody>
    <tr data-lake-id="ue5ecb9c8" id="ue5ecb9c8">
     <td data-lake-id="ub38e99f1" id="ub38e99f1">
      <p data-lake-id="u66a512e8" id="u66a512e8"><span data-lake-id="uc0b6c471" id="uc0b6c471">字段</span></p></td>
     <td data-lake-id="u229b15cf" id="u229b15cf">
      <p data-lake-id="u9f27831b" id="u9f27831b"><span data-lake-id="u0b58b26c" id="u0b58b26c">类型</span></p></td>
     <td data-lake-id="ue2380bb1" id="ue2380bb1">
      <p data-lake-id="u3983df48" id="u3983df48"><span data-lake-id="uc8416958" id="uc8416958">是否可空</span></p></td>
    </tr>
    <tr data-lake-id="u33fc6954" id="u33fc6954">
     <td data-lake-id="ub5d4483f" id="ub5d4483f">
      <p data-lake-id="ud83e9a60" id="ud83e9a60"><span data-lake-id="ueb71eff5" id="ueb71eff5">id</span></p></td>
     <td data-lake-id="u0c7e3413" id="u0c7e3413">
      <p data-lake-id="u738e540a" id="u738e540a"><span data-lake-id="ue8239b3b" id="ue8239b3b">long</span></p></td>
     <td data-lake-id="ud37fb7df" id="ud37fb7df">
      <p data-lake-id="u48a74b54" id="u48a74b54"><span data-lake-id="u3f421754" id="u3f421754">否</span></p></td>
    </tr>
    <tr data-lake-id="u5f4b2047" id="u5f4b2047" style="height: 37px">
     <td data-lake-id="u243c4b60" id="u243c4b60">
      <p data-lake-id="u01d3d5fd" id="u01d3d5fd"><span data-lake-id="u2c60c67d" id="u2c60c67d">gmt_create</span></p></td>
     <td data-lake-id="u904595fa" id="u904595fa">
      <p data-lake-id="u6ae20222" id="u6ae20222"><span data-lake-id="ud362c8a1" id="ud362c8a1">date</span></p></td>
     <td data-lake-id="uac36fb0c" id="uac36fb0c">
      <p data-lake-id="u92baa628" id="u92baa628"><span data-lake-id="u9ea456f2" id="u9ea456f2">否</span></p></td>
    </tr>
    <tr data-lake-id="u2d29bdc3" id="u2d29bdc3" style="height: 37px">
     <td data-lake-id="u85f18a55" id="u85f18a55">
      <p data-lake-id="u98dfec25" id="u98dfec25"><span data-lake-id="u7dbed281" id="u7dbed281">gmt_modified</span></p></td>
     <td data-lake-id="uade643a7" id="uade643a7">
      <p data-lake-id="ube15fcc6" id="ube15fcc6"><span data-lake-id="u9546fe7c" id="u9546fe7c">date</span></p></td>
     <td data-lake-id="ueb97da5a" id="ueb97da5a">
      <p data-lake-id="u39287a5a" id="u39287a5a"><span data-lake-id="u4fed279f" id="u4fed279f">否</span></p></td>
    </tr>
    <tr data-lake-id="u06fe2ecd" id="u06fe2ecd" style="height: 37px">
     <td data-lake-id="u0a692ea5" id="u0a692ea5">
      <p data-lake-id="u84ce583b" id="u84ce583b"><span data-lake-id="u82e57820" id="u82e57820">user_id</span></p></td>
     <td data-lake-id="uefabd4df" id="uefabd4df">
      <p data-lake-id="u1bc06ee5" id="u1bc06ee5"><span data-lake-id="u5c99d52c" id="u5c99d52c">varchar</span></p></td>
     <td data-lake-id="u8a7b0e00" id="u8a7b0e00">
      <p data-lake-id="u1d18ef10" id="u1d18ef10"><span data-lake-id="u6b28502c" id="u6b28502c">否</span></p></td>
    </tr>
    <tr data-lake-id="ue0418f75" id="ue0418f75" style="height: 37px">
     <td data-lake-id="uf46c3b1c" id="uf46c3b1c">
      <p data-lake-id="u2114a2eb" id="u2114a2eb"><span data-lake-id="ube0a8625" id="ube0a8625">product_code</span></p></td>
     <td data-lake-id="u1f824eaa" id="u1f824eaa">
      <p data-lake-id="ueb0587b8" id="ueb0587b8"><span data-lake-id="u2b14f70b" id="u2b14f70b">varchar</span></p></td>
     <td data-lake-id="u73fff391" id="u73fff391">
      <p data-lake-id="u71cb3e33" id="u71cb3e33"><span data-lake-id="uda0865d1" id="uda0865d1">否</span></p></td>
    </tr>
    <tr data-lake-id="ub75d5760" id="ub75d5760" style="height: 37px">
     <td data-lake-id="ubb41294f" id="ubb41294f">
      <p data-lake-id="u819da44e" id="u819da44e"><span data-lake-id="u219a5203" id="u219a5203">state</span></p></td>
     <td data-lake-id="ue0c5136c" id="ue0c5136c">
      <p data-lake-id="ua5ca07a5" id="ua5ca07a5"><span data-lake-id="ud2938832" id="ud2938832">varchar</span></p></td>
     <td data-lake-id="ue5746667" id="ue5746667">
      <p data-lake-id="uc10fb4bd" id="uc10fb4bd"><span data-lake-id="u8563ffe9" id="u8563ffe9">否</span></p></td>
    </tr>
    <tr data-lake-id="u86afc1a8" id="u86afc1a8" style="height: 37px">
     <td data-lake-id="u45b29761" id="u45b29761">
      <p data-lake-id="ud4981a8e" id="ud4981a8e"><strong><span data-lake-id="u2f5ce2ce" id="u2f5ce2ce">is_deleted</span></strong></p></td>
     <td data-lake-id="u70564131" id="u70564131">
      <p data-lake-id="u20fd9a1d" id="u20fd9a1d"><strong><span data-lake-id="uda266998" id="uda266998">int</span></strong></p></td>
     <td data-lake-id="uea3c009d" id="uea3c009d">
      <p data-lake-id="uc45d3d1c" id="uc45d3d1c"><strong><span data-lake-id="uc56df6ba" id="uc56df6ba">否</span></strong></p></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="u42737613" id="u42737613"><br></p>
  <p data-lake-id="u05508c60" id="u05508c60"><span data-lake-id="u25bde835" id="u25bde835">这个字段一般定义为deleted或者is_deleted，常见的做法是默认是0，表示未删除，如果逻辑删除的话则把他设置为1。</span></p>
  <p data-lake-id="u2c49d40e" id="u2c49d40e"><span data-lake-id="u32d8eb1c" id="u32d8eb1c">​</span><br></p>
  <p data-lake-id="u0a11950c" id="u0a11950c"><span data-lake-id="u7f3c7eb6" id="u7f3c7eb6">那么问题来了， 我们怎么避免用户在开通的时候因为并发导致插入多条开通记录呢？</span></p>
  <p data-lake-id="ub2c9aa91" id="ub2c9aa91"><span data-lake-id="u734fed48" id="u734fed48">​</span><br></p>
  <p data-lake-id="uafcaf0b2" id="uafcaf0b2"><span data-lake-id="u8a62ab31" id="u8a62ab31">当然，我们可以做各种分布式锁，但是我们也提到过，不管怎么做，最终都得在数据库层面做兜底，而一般这种情况，就需要做一个唯一性约束。</span></p>
  <p data-lake-id="u6349f172" id="u6349f172"><span data-lake-id="u649578fc" id="u649578fc">​</span><br></p>
  <p data-lake-id="u57405138" id="u57405138"><span data-lake-id="uebd26010" id="uebd26010">可是同一个用户是可以有多条开通记录的，唯一性约束如果用user_id+product_code 那么就肯定是不行的，会导致用户无法多次开通。</span></p>
  <p data-lake-id="uee3c6df3" id="uee3c6df3"><span data-lake-id="uaaa618d3" id="uaaa618d3">​</span><br></p>
  <p data-lake-id="u6dc968e7" id="u6dc968e7"><span data-lake-id="ue15fe0bc" id="ue15fe0bc">但是如果再加上is_deleted，是可以区分开通和退出了，但是一个用户如果多次退出又不行了，那么该如何支持这种业务需求呢？</span></p>
  <p data-lake-id="u170097b0" id="u170097b0"><span data-lake-id="u32eaf94b" id="u32eaf94b">​</span><br></p>
  <p data-lake-id="u7bd0d691" id="u7bd0d691"><span data-lake-id="u90c63f16" id="u90c63f16">后面一共有四种方案，前两个方案是如果直接做物理删除如何实现业务需求！后面两个是如果就是不能做物理删除该怎么办？</span></p>
  <p data-lake-id="u91b67b0a" id="u91b67b0a"><span data-lake-id="ua8b93e96" id="ua8b93e96">​</span><br></p>
  <p data-lake-id="u0da81cea" id="u0da81cea"><span data-lake-id="u287a9bfb" id="u287a9bfb">如果是题目中的问题的解答，可以直接看3、4两个方案，但是前两个也建议大家看一下，有的时候，面试的时候不只是可以回答面试官的问题，也可以想办法从跟上让他的问题不会发生。</span></p>
  <p data-lake-id="u2f29bd25" id="u2f29bd25"><span data-lake-id="ub761864d" id="ub761864d">​</span><br></p>
  <h3 data-lake-id="QtjvY" id="QtjvY"><span data-lake-id="u378c9545" id="u378c9545">方案一，物理删除+数据归档</span></h3>
  <p data-lake-id="uf0414bc9" id="uf0414bc9"><br></p>
  <p data-lake-id="u4f65bd7e" id="u4f65bd7e"><span data-lake-id="u46e221d8" id="u46e221d8">最简单的方案就是直接做物理删除了，但是物理删除怎么实现数据分析、报表等功能呢，就是说如何做数据留痕呢？</span></p>
  <p data-lake-id="u687303c5" id="u687303c5"><span data-lake-id="u1c33d523" id="u1c33d523">​</span><br></p>
  <p data-lake-id="uc0ecb974" id="uc0ecb974"><span data-lake-id="u0b520693" id="u0b520693">简单的方案就是做数据归档或者离线表。</span></p>
  <p data-lake-id="ue5632986" id="ue5632986"><span data-lake-id="ue3b71be3" id="ue3b71be3">​</span><br></p>
  <p data-lake-id="ubff936fe" id="ubff936fe"><span data-lake-id="u0a5a2e45" id="u0a5a2e45">就是说再创建一张service_record_his表，然后再用户退出时，在同一个事务中把service_record表中的数据插入到service_record_his中，然后再把service_record表中的记录删除：</span></p>
  <p data-lake-id="u94ca1703" id="u94ca1703"><span data-lake-id="uff88eb3f" id="uff88eb3f">​</span><br></p>
  <pre lang="java"><code>
INSERT INTO service_record_his
SELECT * FROM service_record WHERE user_id = 'xxx' and product_code = "";

DELETED FROM service_record WHERE user_id = 'xxx' and product_code = ""; 
</code></pre>
  <p data-lake-id="u4b42fa65" id="u4b42fa65"><br></p>
  <p data-lake-id="ufabd72fc" id="ufabd72fc"><span data-lake-id="u91d1ce3d" id="u91d1ce3d">这样我们就可以基于service_record_his进行数据分析和做报表了。</span></p>
  <p data-lake-id="u861021c2" id="u861021c2"><span data-lake-id="uf35e379d" id="uf35e379d">​</span><br></p>
  <p data-lake-id="uad767d0c" id="uad767d0c"><span data-lake-id="uc68b6bf0" id="uc68b6bf0">还有一种归档方式，就是通过离线数仓来做，比如我们的业务是这样做的：</span></p>
  <p data-lake-id="u9e228c10" id="u9e228c10"><span data-lake-id="uccc7e5e9" id="uccc7e5e9">​</span><br></p>
  <p data-lake-id="ubaf37b9a" id="ubaf37b9a"><span data-lake-id="uf78ab7a6" id="uf78ab7a6">1、在线表每天凌晨都向离线数仓做一次数据同步</span></p>
  <p data-lake-id="u9f65eeb2" id="u9f65eeb2"><span data-lake-id="u54daa6c7" id="u54daa6c7">2、同一个用户当天退出后不能立刻开通（一般业务上都有这个限制，有的业务时3天，有的是15天，避免用户刷新人优惠之类的，对于我们的金融业务，也避免用户挑保，这个就不展开说了。。。）</span></p>
  <p data-lake-id="u5e508727" id="u5e508727"><span data-lake-id="u4350471e" id="u4350471e">3、数仓同步时，数据的insert、update做同步，但是delete不做同步</span></p>
  <p data-lake-id="u74c2d333" id="u74c2d333"><span data-lake-id="u31d59fb7" id="u31d59fb7">​</span><br></p>
  <p data-lake-id="u29ae5293" id="u29ae5293"><span data-lake-id="u49c743ec" id="u49c743ec">以上其实并不是我们为了做这个需求特意做的约束，而是我们的业务和数仓同步逻辑他就是这样的，所以这样的话不需要一张service_record_his表了，直接做物理删除就行了。</span></p>
  <p data-lake-id="u14ad04ea" id="u14ad04ea"><span data-lake-id="u4a23cdad" id="u4a23cdad">​</span><br></p>
  <p data-lake-id="u252e597e" id="u252e597e"><span data-lake-id="u75e005f4" id="u75e005f4">当然，这个方案存在一个小问题，一般离线数仓都是每天陵城做一次整体的数据同步，所以，如果当天开通后马上退出，其实是有问题的（因为离线数仓还没来得及同步），所以我们在业务上限制当天不能退出。还有的是限制当天退出后不能再开通，然后当天的退出记录不立即delete，而是当天凌晨数仓同步后再delete。还有的就是可以在用户注册之后马上触发一次离线表同步（这个方案用的很少，不建议）。</span></p>
  <p data-lake-id="u33deb8c3" id="u33deb8c3"><span data-lake-id="u84ee5527" id="u84ee5527">​</span><br></p>
  <h3 data-lake-id="IWLjw" id="IWLjw"><span data-lake-id="uc1abf431" id="uc1abf431">方案二，复用一条开通记录+记录开通流水</span></h3>
  <p data-lake-id="ue5eefff6" id="ue5eefff6"><br></p>
  <p data-lake-id="u6e86d1ab" id="u6e86d1ab"><span data-lake-id="u2d2c542f" id="u2d2c542f">为了解决我们的数据留痕的问题，不一定非要基于service_record这张主表，像我们的业务来说，基本上一些核心操作都是有流水表的，如service_record_stream，用户相关的各种操作，开通、退出、冻结、解冻等等都插入一条流水。</span></p>
  <p data-lake-id="udd5e2c15" id="udd5e2c15"><span data-lake-id="u93ad7c1d" id="u93ad7c1d">​</span><br></p>
  <p data-lake-id="ub5389a05" id="ub5389a05"><span data-lake-id="uc3cffc28" id="uc3cffc28">这样我们就可以保证service_record表中针对同一个用户+产品，只会有一条记录，用户开通后，状态为ACTIVE，用户退出后，状态为QUIT。用户重新开通，再更新为ACTIVE。</span></p>
  <p data-lake-id="u5f952322" id="u5f952322"><span data-lake-id="u992256f9" id="u992256f9">​</span><br></p>
  <p data-lake-id="ua6f30577" id="ua6f30577"><span data-lake-id="ua48b73de" id="ua48b73de">然后所以数据的分析需求，都基于流水表来做，流水表一般比主表多几个字段。如：</span></p>
  <p data-lake-id="u2863721a" id="u2863721a"><br></p>
  <table data-lake-id="HyVtV" id="HyVtV" margin="true" width-mode="contain" class="lake-table" style="width: 750px">
   <colgroup>
    <col width="250">
    <col width="250">
    <col width="250">
   </colgroup>
   <tbody>
    <tr data-lake-id="u985713e8" id="u985713e8">
     <td data-lake-id="u39a17775" id="u39a17775">
      <p data-lake-id="ud0950f63" id="ud0950f63"><span data-lake-id="u9435cefe" id="u9435cefe">字段</span></p></td>
     <td data-lake-id="ud652edba" id="ud652edba">
      <p data-lake-id="udede90db" id="udede90db"><span data-lake-id="ue507b02a" id="ue507b02a">类型</span></p></td>
     <td data-lake-id="uce46967c" id="uce46967c">
      <p data-lake-id="ubb76c87f" id="ubb76c87f"><span data-lake-id="u9398dad0" id="u9398dad0">是否可空</span></p></td>
    </tr>
    <tr data-lake-id="ucfa6fc5a" id="ucfa6fc5a">
     <td data-lake-id="uff352072" id="uff352072">
      <p data-lake-id="u4700eabf" id="u4700eabf"><span data-lake-id="u7ca40377" id="u7ca40377">id</span></p></td>
     <td data-lake-id="u390427b6" id="u390427b6">
      <p data-lake-id="u8df68680" id="u8df68680"><span data-lake-id="u7b3d9e95" id="u7b3d9e95">long</span></p></td>
     <td data-lake-id="uca7cb656" id="uca7cb656">
      <p data-lake-id="u477661c5" id="u477661c5"><span data-lake-id="ua545d994" id="ua545d994">否</span></p></td>
    </tr>
    <tr data-lake-id="u9ab8a742" id="u9ab8a742" style="height: 37px">
     <td data-lake-id="ud3ec0b93" id="ud3ec0b93">
      <p data-lake-id="ua6f60474" id="ua6f60474"><span data-lake-id="ue742c381" id="ue742c381">gmt_create</span></p></td>
     <td data-lake-id="uaa0142d0" id="uaa0142d0">
      <p data-lake-id="u32cc497c" id="u32cc497c"><span data-lake-id="u5b15930b" id="u5b15930b">date</span></p></td>
     <td data-lake-id="u6497201f" id="u6497201f">
      <p data-lake-id="u73346d0b" id="u73346d0b"><span data-lake-id="ua639a7ac" id="ua639a7ac">否</span></p></td>
    </tr>
    <tr data-lake-id="u25ffc8a8" id="u25ffc8a8" style="height: 37px">
     <td data-lake-id="u104e1e41" id="u104e1e41">
      <p data-lake-id="u4707d91c" id="u4707d91c"><span data-lake-id="u183556f9" id="u183556f9">gmt_modified</span></p></td>
     <td data-lake-id="udef08ac7" id="udef08ac7">
      <p data-lake-id="ufd344f6e" id="ufd344f6e"><span data-lake-id="uf8ae7a34" id="uf8ae7a34">date</span></p></td>
     <td data-lake-id="u03d5e3c7" id="u03d5e3c7">
      <p data-lake-id="uf990e570" id="uf990e570"><span data-lake-id="u6d468923" id="u6d468923">否</span></p></td>
    </tr>
    <tr data-lake-id="u7eb8a04d" id="u7eb8a04d" style="height: 37px">
     <td data-lake-id="udd734dce" id="udd734dce">
      <p data-lake-id="u79e28853" id="u79e28853"><span data-lake-id="uababfe48" id="uababfe48">user_id</span></p></td>
     <td data-lake-id="u6a11a90d" id="u6a11a90d">
      <p data-lake-id="u160f8e73" id="u160f8e73"><span data-lake-id="ub22ac51d" id="ub22ac51d">varchar</span></p></td>
     <td data-lake-id="ub4ca9aa1" id="ub4ca9aa1">
      <p data-lake-id="u67c80b3e" id="u67c80b3e"><span data-lake-id="u33091a39" id="u33091a39">否</span></p></td>
    </tr>
    <tr data-lake-id="u9fec4d9a" id="u9fec4d9a" style="height: 37px">
     <td data-lake-id="u5e55b1b9" id="u5e55b1b9">
      <p data-lake-id="uffdb59cb" id="uffdb59cb"><span data-lake-id="u65016656" id="u65016656">product_code</span></p></td>
     <td data-lake-id="u9b670872" id="u9b670872">
      <p data-lake-id="u9f101723" id="u9f101723"><span data-lake-id="uae641caa" id="uae641caa">varchar</span></p></td>
     <td data-lake-id="u65446d5b" id="u65446d5b">
      <p data-lake-id="ud62f19aa" id="ud62f19aa"><span data-lake-id="ueca17756" id="ueca17756">否</span></p></td>
    </tr>
    <tr data-lake-id="u68a67ab2" id="u68a67ab2" style="height: 37px">
     <td data-lake-id="uc0a4bb87" id="uc0a4bb87">
      <p data-lake-id="u975cd2ea" id="u975cd2ea"><span data-lake-id="u24f6c55b" id="u24f6c55b">state</span></p></td>
     <td data-lake-id="u5e5a0b10" id="u5e5a0b10">
      <p data-lake-id="uc41f883b" id="uc41f883b"><span data-lake-id="u59fe764a" id="u59fe764a">varchar</span></p></td>
     <td data-lake-id="u0f1d7be3" id="u0f1d7be3">
      <p data-lake-id="u2d3335e6" id="u2d3335e6"><span data-lake-id="ue70bef6b" id="ue70bef6b">否</span></p></td>
    </tr>
    <tr data-lake-id="u1abac49f" id="u1abac49f" style="height: 37px">
     <td data-lake-id="ude4c236c" id="ude4c236c">
      <p data-lake-id="u6d1c652b" id="u6d1c652b"><strong><span data-lake-id="uea3136ad" id="uea3136ad">stream_type</span></strong></p></td>
     <td data-lake-id="u6ec44387" id="u6ec44387">
      <p data-lake-id="u2741f11b" id="u2741f11b"><strong><span data-lake-id="u5c4b526d" id="u5c4b526d">varchar</span></strong></p></td>
     <td data-lake-id="u95163151" id="u95163151">
      <p data-lake-id="uaaceb20d" id="uaaceb20d"><strong><span data-lake-id="u2225ae88" id="u2225ae88">否</span></strong></p></td>
    </tr>
   </tbody>
  </table>
  <p data-lake-id="u88a145eb" id="u88a145eb"><br></p>
  <p data-lake-id="u6f2ecd83" id="u6f2ecd83"><br></p>
  <h3 data-lake-id="nqSV4" id="nqSV4"><span data-lake-id="ua60b2c90" id="ua60b2c90">方案三，&gt;0都表示逻辑删除</span></h3>
  <p data-lake-id="uc7ae88e8" id="uc7ae88e8"><br></p>
  <p data-lake-id="u9bfbeb9e" id="u9bfbeb9e"><span data-lake-id="ue3b292a1" id="ue3b292a1">前面两个方案都是绕开了我们的问题，那么能不能直接通过加唯一性约束的方式解决？</span></p>
  <p data-lake-id="udf1ad6c2" id="udf1ad6c2"><span data-lake-id="ufdc8d937" id="ufdc8d937">​</span><br></p>
  <p data-lake-id="u95dce20f" id="u95dce20f"><span data-lake-id="u17ec0de6" id="u17ec0de6">有一个办法，那就是之所以我们没办法把user_id+product_code+is_deleted作为一个联合的唯一索引，主要是因为is_deleted只有0和1两种情况</span></p>
  <p data-lake-id="u7b138b69" id="u7b138b69"><span data-lake-id="u125c090a" id="u125c090a">​</span><br></p>
  <p data-lake-id="ueafce94c" id="ueafce94c"><span data-lake-id="u61efdba5" id="u61efdba5">那只要解决这个问题就行了，核心的思想就是0表示未删除，只要大于0都表示删除。</span></p>
  <p data-lake-id="u21484ba4" id="u21484ba4"><span data-lake-id="uf83958bc" id="uf83958bc">​</span><br></p>
  <p data-lake-id="u5f278e90" id="u5f278e90"><span data-lake-id="u6efaac34" id="u6efaac34">那么就可以在每次删除之后，is_deleted都+1，is_deleted=1表示第一次退出，is_deleted=2表示第2次退出。。。。</span></p>
  <p data-lake-id="u9a88803d" id="u9a88803d"><span data-lake-id="u6ac236ab" id="u6ac236ab">​</span><br></p>
  <p data-lake-id="u8f618c40" id="u8f618c40"><span data-lake-id="u99ceb941" id="u99ceb941">这么做还是有点麻烦，而且也可能有一个并发情况下导致这个id重复了，虽然概率很低很低。但是我们前面说了，数据库唯一性约束用于兜底，该有的分布式锁还是有的，所以并发问题倒也还好。</span></p>
  <p data-lake-id="u8b0b0ab9" id="u8b0b0ab9"><span data-lake-id="u336424cb" id="u336424cb">​</span><br></p>
  <p data-lake-id="u10d5f330" id="u10d5f330"><span data-lake-id="u21dc57d7" id="u21dc57d7">不过还是可以简单点，那就是在用户退出的时候，直接把这条service_record的主键（前提需要时自增的ID）设置到is_deleted中。</span></p>
  <p data-lake-id="u57c724b9" id="u57c724b9"><br></p>
  <p data-lake-id="u3c2547f4" id="u3c2547f4"><span data-lake-id="u77586262" id="u77586262">i，is_deleted=43532表示用户退出，is_deleted=64532也表示退出了。。。。</span></p>
  <p data-lake-id="u9c5332d1" id="u9c5332d1"><span data-lake-id="u473fafb4" id="u473fafb4">​</span><br></p>
  <p data-lake-id="u5bb3e3d8" id="u5bb3e3d8"><span data-lake-id="ue17dbb1c" id="ue17dbb1c">​</span><br></p>
  <h3 data-lake-id="YIRij" id="YIRij"><span data-lake-id="u1c7f06b4" id="u1c7f06b4">方案四，引入一个新的字段</span></h3>
  <p data-lake-id="u6f9b13ba" id="u6f9b13ba"><br></p>
  <p data-lake-id="u006858e1" id="u006858e1"><span data-lake-id="ufe58b12a" id="ufe58b12a">除了上面的方式是改造is_deleted以外，还有一个办法，那就是引入一个新的字段，如deleted_time表示这条记录被删除的时间戳</span></p>
  <p data-lake-id="u76569aef" id="u76569aef"><span data-lake-id="uc809ca9d" id="uc809ca9d">​</span><br></p>
  <p data-lake-id="u673b255b" id="u673b255b"><span data-lake-id="u27021132" id="u27021132">然后user_id+product_code+is_deleted+deleted_time一起，做一个联合的唯一索引。</span></p>
  <p data-lake-id="u2584ab10" id="u2584ab10"><span data-lake-id="u2fb39e44" id="u2fb39e44">​</span><br></p>
  <p data-lake-id="ua19a19c6" id="ua19a19c6"><span data-lake-id="u00b29af6" id="u00b29af6">当然，你也可以把is_deleted去掉，然后是否被逻辑删除通过deleted_time是否为空来判断，那么就又和第三个方案有点像了。</span></p>
  <p data-lake-id="u89bd65b6" id="u89bd65b6"><span data-lake-id="ucc72ae6a" id="ucc72ae6a">​</span><br></p>
  <h3 data-lake-id="aT6Ux" id="aT6Ux"><span data-lake-id="u8fe47b3a" id="u8fe47b3a">总结</span></h3>
  <p data-lake-id="ucdb91597" id="ucdb91597"><br></p>
  <p data-lake-id="ufd9f9746" id="ufd9f9746"><span data-lake-id="u263405b2" id="u263405b2">以上提了几个方案，其实各有优缺点，有的需要引入一张新表，有的需要自己控制并发， 有的需要依赖数仓能力，有的需要增加冗余字段。</span></p>
  <p data-lake-id="u461b1942" id="u461b1942"><span data-lake-id="u321116ce" id="u321116ce">​</span><br></p>
  <p data-lake-id="u31c41ed0" id="u31c41ed0"><span data-lake-id="u85322dc4" id="u85322dc4">总之都各自有优缺点，反正这几个方案我们都用过，不同的业务逻辑可能用的也不太一样。有的也多个方案做组合，比如：历史表+流水表+数仓、 流水表+&gt;0逻辑删除。</span></p>
 </body>
</html>